/*
 * Copyright (C) 2024, KylinSoft Co., Ltd.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * Authors: zy-yuan1 <zhangyuanyuan1@kylinos.cn>
 *
 */
#include "neetspeed.h"
#include <NetworkManagerQt/Manager>
#include <networkmanagerqt/networkmanagerqt_export.h>
#include <QDebug>

#define REFRESH_NETSPEED_TIMER 1000
#define LOG_FLAG "[ukui-netspeed]"

static Neetspeed* m_instance = nullptr;
Neetspeed *Neetspeed::instance()
{
    if (!m_instance) {
        m_instance = new Neetspeed();
    }
    return m_instance;
}

Neetspeed::Neetspeed(QObject *parent)
{
    getNetworkDeviceInterface();
    connect(NetworkManager::notifier(), &NetworkManager::Notifier::primaryConnectionChanged, this, &Neetspeed::onPrimaryConnectionChanged);

    m_showNetspeedtimer = new QTimer();
    m_showNetspeedtimer->setTimerType(Qt::PreciseTimer);
    connect(m_showNetspeedtimer, &QTimer::timeout, [&]() {
        calculateNetspeed(m_deviceName, m_strRxRate, m_strTxRate);
    });
    m_showNetspeedtimer->start(REFRESH_NETSPEED_TIMER);
}

Neetspeed::~Neetspeed()
{
    if (m_showNetspeedtimer != nullptr) {
        if (m_showNetspeedtimer->isActive()) {
            m_showNetspeedtimer->stop();
        }
        delete m_showNetspeedtimer;
        m_showNetspeedtimer = nullptr;
    }
}

void Neetspeed::startShowNetspeed()
{
    m_showNetspeedtimer->start(REFRESH_NETSPEED_TIMER);
}

void Neetspeed::stopShowNetspeed()
{
    if (m_showNetspeedtimer->isActive()) {
        m_showNetspeedtimer->stop();
    }
}

QString Neetspeed::rxRate()
{
    return m_strRxRate;
}

QString Neetspeed::txRate()
{
    return m_strTxRate;
}

void Neetspeed::getNetworkDeviceInterface()
{
    NetworkManager::ActiveConnection::Ptr activeConnectPtr = nullptr;
    activeConnectPtr = NetworkManager::primaryConnection();
    if (activeConnectPtr.isNull() || activeConnectPtr->devices().isEmpty()) {
        return;
    }
    QString deviceUni = activeConnectPtr->devices().at(0);
    m_deviceName = findNetworkDeviceByUni(deviceUni);
}

QString Neetspeed::findNetworkDeviceByUni(const QString uni)
{
    for (auto const &device : NetworkManager::networkInterfaces()) {
        if (device.isNull()) {
            continue;
        }
        if (device->uni() == uni) {
            NetworkManager::Device::Ptr devPtr = device;
            if (!devPtr.isNull()) {
                return devPtr->interfaceName();
            }
        }
    }
    return QString();
}

int Neetspeed::getCurrentLoadRate(QString dev, long *rxRate, long *txRate)
{
    FILE * netDevFile; //文件指针
    char buffer[1024]; //文件中的内容暂存在字符缓冲区里
    //size_t bytes_read; //实际读取的内容大小
    char * match; //用以保存所匹配字符串及之后的内容
    char * device;//将QString转为Char *
    QByteArray ba = dev.toLatin1(); // must
    device = ba.data();
    int counter = 0;
    char tmpValue[128];

    if ((NULL == device) || (NULL == rxRate) || (NULL == txRate)) {
        qDebug() << LOG_FLAG << "parameter pass error" ;
        return -1;
    }

    if ((netDevFile = fopen("/proc/net/dev", "r")) == NULL) {
        //打开文件/pro/net/dev/，从中读取流量数据
        qDebug() << LOG_FLAG << "error occurred when try to open file /proc/net/dev/";
        return -1;
    }
    memset(buffer, 0, sizeof(buffer));

    while (fgets(buffer, sizeof(buffer), netDevFile) != NULL) {
        match = strstr(buffer, device);

        if (NULL == match) {
            // qDebug() << "No eth0 keyword to find!";
            continue;
        } else {
            match = match + strlen(device) + strlen(":"); //地址偏移到冒号
            sscanf(match, "%ld ", rxRate);
            memset(tmpValue, 0, sizeof(tmpValue));
            sscanf(match, "%s ", tmpValue);
            match = match + strlen(tmpValue);
            for (size_t i = 0; i < strlen(buffer); ++i) {
                if (0x20 == *match) {
                    match ++;
                } else {
                    if (8 == counter) {
                        sscanf(match, "%ld ", txRate);
                    }
                    memset(tmpValue, 0, sizeof(tmpValue));
                    sscanf(match, "%s ", tmpValue);
                    match = match + strlen(tmpValue);
                    counter++;
                }
            }
        }
    }

    fclose(netDevFile);
    netDevFile = nullptr;
    return 0;
}

void Neetspeed::calculateNetspeed(QString dev, QString &strRx, QString &strTx)
{
    if (dev.isEmpty()) {
        qWarning() << "network device is empty, calculateNetspeed failed!";
        return;
    }

    if (getCurrentLoadRate(dev, &m_startRxRate, &m_startTxRate) == -1) {
        m_startRxRate = m_endRxRate;
        return;
    }

    long int deltaRx = (m_startRxRate - m_endRxRate) / 1024;
    long int deltaTx = (m_startTxRate - m_endTxRate) / 1024;

    //简易滤波
    if (deltaRx < 0 || deltaTx < 0) {
        deltaRx = 0;
        deltaTx = 0;
    }
    else if (m_endRxRate == 0 || m_endTxRate == 0){
        deltaRx = 0;
        deltaTx = 0;
    }

    m_endRxRate = m_startRxRate;
    m_endTxRate = m_startTxRate;

    int rxNum = deltaRx;
    int txNum = deltaTx;

    if (rxNum < 1024) {
         strRx = QString::number(rxNum) + "KB/s";
    } else {
        int remainder;
        if (rxNum % 1024 < 100) {
            remainder = 0;
        } else {
            remainder = (rxNum % 1024) / 100;
        }
        strRx = QString::number(rxNum / 1024) + "."  + QString::number(remainder) + "MB/s";
    }

    if (txNum < 1024) {
        strTx = QString::number(txNum) + "KB/s";
    } else {
        int remainder;
        if (txNum % 1024 < 100) {
            remainder = 0;
        } else {
            remainder = (txNum % 1024)/100;
        }
        strTx = QString::number(txNum / 1024) + "."  + QString::number(remainder) + "MB/s";
    }
    Q_EMIT netspeedUpdated();
}

void Neetspeed::onPrimaryConnectionChanged(const QString &uni)
{
    Q_UNUSED(uni) //active connecetion uni, not device uni
    getNetworkDeviceInterface();
}


